#include "stdafx.h"
#include "mesh.h"

Mesh::Mesh(const MDagPath &meshPath,MSpace::Space space)
{
	MStatus res;

	MFnMesh mesh(meshPath,&res);
	if(res != MStatus::kSuccess)
	{
		throw Exception(std::string(meshPath.partialPathName().asChar()) + 
			" isn't a mesh");
	}

	bool flipWindingOrder = true;
	if(space == MSpace::kWorld)
	{
		MMatrix meshMat = meshPath.inclusiveMatrix(&res);
		assert(res == MStatus::kSuccess);

		if(meshMat.det3x3() < 0)
			flipWindingOrder = false;
	}

	MObjectArray shaders;
	MIntArray shaderIndices;
	res = mesh.getConnectedShaders(0,shaders,shaderIndices);
	assert(res == MStatus::kSuccess);

	std::vector<Material*> materialByIndex;
	for(int i = 0;i < (int)shaders.length();i++)
	{
		MFnDependencyNode shaderNode(shaders[i],&res);
		assert(res == MStatus::kSuccess);

		Material *pMat = new Material;

		// get the name
		pMat->name = shaderNode.name(&res).asChar();
		assert(res == MStatus::kSuccess);

		materials[pMat->name] = pMat;
		materialByIndex.push_back(pMat);
	}

	int nPols = (int)mesh.numPolygons(&res);
	assert(res == MStatus::kSuccess);
	for(int i = 0;i < nPols;i++)
	{
		Material *pMat = materialByIndex[shaderIndices[i]];

		int nTris = (int)mesh.polygonVertexCount(i,&res) - 2;
		assert(res == MStatus::kSuccess);
		for(int j = 0;j < nTris;j++)
		{
			Triangle tri;

			int verts[3];
			res = mesh.getPolygonTriangleVertices(i,j,verts);
			assert(res == MStatus::kSuccess);

			for(int k = 0;k < 3;k++)
			{
				res = mesh.getPoint(verts[k],tri.verts[k].pos,space);
				assert(res == MStatus::kSuccess);
				
				tri.verts[k].pos.z = -tri.verts[k].pos.z;
			}

			if(flipWindingOrder)
				std::swap(tri.verts[1],tri.verts[2]);

			pMat->triangles.push_back(tri);
		}
	}
}

Mesh::~Mesh()
{
	for(MaterialIt it = materials.begin();it != materials.end();++it)
		delete it->second;
}

void Mesh::Union(const Mesh &b)
{
	for(ConstMaterialIt it = b.materials.begin();it != b.materials.end();++it)
	{
		MaterialIt matInThisMesh = materials.find(it->first);
		Material *pDestMat;
		if(matInThisMesh == materials.end())
		{
			pDestMat = new Material(*it->second);
			materials[pDestMat->name] = pDestMat;
		}
		else
			pDestMat = matInThisMesh->second;		

		for(int i = 0;i < (int)it->second->triangles.size();i++)
		{
			pDestMat->triangles.push_back(
				it->second->triangles[i]);
		}
	}
}

void Mesh::WriteToXml(const std::string &outFileName)
{
	TiXmlDocument doc(outFileName);

	TiXmlElement *pRoot = new TiXmlElement("didaMesh");
	doc.LinkEndChild(pRoot);
	
	for(MaterialIt matIt = materials.begin();matIt != materials.end();++matIt)
	{
		TiXmlElement *pMaterialXml = new TiXmlElement("material");
		pRoot->LinkEndChild(pMaterialXml);
		pMaterialXml->SetAttribute("name",matIt->second->name);

		Material *pCurMat = matIt->second;
		for(int j = 0;j < (int)pCurMat->triangles.size();j++)
		{
			const Triangle &tri = pCurMat->triangles[j];

			TiXmlElement *pTriangleXml = new TiXmlElement("triangle");
			pMaterialXml->LinkEndChild(pTriangleXml);
			for(int k = 0;k < 3;k++)
			{
				TiXmlElement *pVertexXml = new TiXmlElement("vertex");
				pTriangleXml->LinkEndChild(pVertexXml);

				std::stringstream s;
				s << tri.verts[k].pos.x << ", " << tri.verts[k].pos.y << 
					", " << tri.verts[k].pos.z;
				pVertexXml->SetAttribute("pos",s.str());
			}
		}
	}

	if(!doc.SaveFile(outFileName.c_str()))
		throw Exception("Couldn't write to " + outFileName);
}

void Mesh::WriteToBinary(const std::string &outFileName)
{
	FILE *pFile = fopen(outFileName.c_str(),"wb");
	if(pFile == 0)
		throw Exception("Couldn't open " + outFileName);

	try
	{
		if(fwrite("DidaMesh",1,8,pFile) != 8)
			throw WE_WRITE_FAILED;

		// write the materials.

		// first the number of materials
		unsigned long count = (unsigned long)materials.size();
		if(fwrite(&count,sizeof(unsigned long),1,pFile) != 1)
			throw WE_WRITE_FAILED;

		// the materials
		unsigned long totalNTris = 0;
		for(MaterialIt matIt = materials.begin();matIt != materials.end();++matIt)
		{
			Material *pCurMat = matIt->second;

			WriteString(pFile,pCurMat->name);
			
			// write the index of the first tri
			if(fwrite(&totalNTris,sizeof(unsigned long),1,pFile) != 1)
				throw WE_WRITE_FAILED;

			unsigned long num = (unsigned long)pCurMat->triangles.size();
			if(fwrite(&num,sizeof(unsigned long),1,pFile) != 1)
				throw WE_WRITE_FAILED;

			totalNTris += num;
		}

		// now write the actual triangles as a big flat list.

		if(fwrite(&totalNTris,sizeof(unsigned long),1,pFile) != 1)
			throw WE_WRITE_FAILED;
		for(MaterialIt matIt = materials.begin();matIt != materials.end();++matIt)
		{
			Material *pCurMat = matIt->second;

			for(int j = 0;j < (int)pCurMat->triangles.size();j++)
			{
				for(int k = 0;k < 3;k++)
				{
					float pos[3];
					pos[0] = (float)pCurMat->triangles[j].verts[k].pos.x;
					pos[1] = (float)pCurMat->triangles[j].verts[k].pos.y;
					pos[2] = (float)pCurMat->triangles[j].verts[k].pos.z;
					if(fwrite(pos,sizeof(float),3,pFile) != 3)
						throw WE_WRITE_FAILED;
				}
			}
		}

		fclose(pFile);
	}
	catch(WriteError writeError)
	{
		fclose(pFile);

		switch(writeError)
		{
		case WE_WRITE_FAILED:
			throw Exception("Couldn't write to " + outFileName);
		}
	}
	catch(...)
	{
		fclose(pFile);
		throw;
	}
}